﻿//--------------------------------------------------------------------------------
// This file is part of the MusicWho - MusicComposer.
//
// Copyright © 2010 Alex-Joyce. All rights reserved.
//
// For information about this application and licensing, go to http://musicwho.codeplex.com.
//
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
//--------------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MWCore
{
    public struct SpikeNote
    {
        public double LongRate;
        public double ShortRate;
        public double NRateVar;
    }

    public struct SpikeComposeParameter
    {
        public double LongIntegrateTime;
        public double ShortIntegrateTime;
    }

    public struct SpikePlayParameter
    {
        public double key;
    }


    public class SpikeComposer 
    {
        List< List<double>> _spiketrains;
        List< List<SpikeNote>> _spikescores;
        SpikeComposeParameter _scparam;


        public List< List<double>> SpikeTrains
        {
            get { return _spiketrains; }
            set
            {
                SpikeScores.Clear();
                _spiketrains = value;
            }
        }

        public List< List<SpikeNote>> SpikeScores
        {
            get { return _spikescores; }
        }

        public SpikeComposeParameter SpikeComposeParameter
        {
            get{return _scparam;}
        }


        public SpikeComposer(List<double> spiketrain)
        {
            List<List<double>> t = new List<List<double>>();
            t.Add(spiketrain);
            _spiketrains = t;
            _spikescores = new List<List<SpikeNote>>();
        }

        public SpikeComposer(List<List<double>> spiketrains)
        {
            _spiketrains = spiketrains;
            _spikescores = new List<List<SpikeNote>>();
        }

        public List<List<SpikeNote>> Compose(SpikeComposeParameter scparam)
        {
            _scparam = scparam;

            List<List<SpikeNote>> sns = new List<List<SpikeNote>>();
            for (int j = 0; j < SpikeTrains.Count; j++)
            {
                List<SpikeNote> jsn = new List<SpikeNote>();
                for (int i = 0; i < SpikeTrains[j].Count; i++)
                {
                    var longspike = SpikeTrains[j].FindAll(new Predicate<double>(x => x > SpikeTrains[j][i] - scparam.LongIntegrateTime / 2.0 && x < SpikeTrains[j][i] + scparam.LongIntegrateTime / 2.0));
                    var shortspike = SpikeTrains[j].FindAll(new Predicate<double>(x => x > SpikeTrains[j][i] - scparam.ShortIntegrateTime / 2.0 && x < SpikeTrains[j][i] + scparam.ShortIntegrateTime / 2.0));
                    SpikeNote sn = new SpikeNote();
                    sn.LongRate = longspike.Count / scparam.LongIntegrateTime;
                    sn.ShortRate = shortspike.Count / scparam.ShortIntegrateTime;
                    sn.NRateVar = (sn.ShortRate - sn.LongRate) / sn.LongRate;
                    jsn.Add(sn);
                }
                sns.Add(jsn);
            }

            _spikescores = sns;
            return _spikescores;
        }

        public List<List<SpikeNote>> Compose(List<List<double>> spiketrains,SpikeComposeParameter scparam)
        {
            SpikeTrains = spiketrains;
            return Compose(scparam);
        }
    }

    public class SpikePlayer
    {
        List< List<SpikeNote>> _spikescores;
        List<byte> _music;
        SpikePlayParameter _spparam;


        public List< List<SpikeNote>> SpikeScores
        {
            get { return _spikescores; }
            set
            {
                _music.Clear();
                _spikescores = value;
            }
        }

        public List<byte> Music
        {
            get { return _music; }
        }

        public SpikePlayParameter SpikePlayParameter
        {
            get { return _spparam; }
        }


        public SpikePlayer(List<SpikeNote> spikescore)
        {
            List<List<SpikeNote>> t = new List<List<SpikeNote>>();
            t.Add(spikescore);
            _spikescores = t;
            _music = new List<byte>();
        }

        public SpikePlayer(List<List<SpikeNote>> spikescores)
        {
            _spikescores = spikescores;
            _music = new List<byte>();
        }

        public List<byte> Play(SpikePlayParameter spparam)
        {
            _spparam = spparam;

            short ntrack = (short)SpikeScores.Count;
            short format = 0;
            if (ntrack > 1)
            {
                 format = 1;
            }
            var header = MIDI.Header(format, ntrack, MIDI.TIMEDIVISION4TH);

            for (int j = 0; j < SpikeScores.Count; j++)
            {
                List<byte> trackcontent = new List<byte>();

                trackcontent.AddRange(MIDI.Text(0, "This music is composed by neurons using MusicWho."));
                trackcontent.AddRange(MIDI.Tempo(0, 120));
                trackcontent.AddRange(MIDI.KeySignature(0, 0, 0));
                trackcontent.AddRange(MIDI.TimeSignature(0, 4, 4, 24, 8));
                trackcontent.AddRange(MIDI.ProgramChange(0, 0, (byte)(j*40)));
                for (int i = 0; i < SpikeScores[j].Count; i++)
                {
                    byte note = (byte)((int)Math.Round(SpikeScores[j][i].NRateVar * 64.0) + 64);
                    trackcontent.AddRange(MIDI.NoteOn((byte)SpikeScores[j][i].LongRate, 0, note, (byte)(SpikeScores[j][i].LongRate + 100)));
                }
                trackcontent.AddRange(MIDI.EndOfTrack(0));

                header.AddRange(MIDI.Track(trackcontent));
            }

            _music = header;
            return _music;
        }

        public List<byte> Play(List<List<SpikeNote>> spikescores,SpikePlayParameter spparam)
        {
            SpikeScores = spikescores;
            return Play(spparam);
        }
    }
}
